﻿namespace MSSqlSchemaDoc.Provider.MicrosoftSqlServer
{
    using System.Collections.ObjectModel;
    using System.Data;
    using System.Data.SqlClient;
    using System.Text;
    using MSSqlSchemaDoc.Core.DataStructures;
    using MSSqlSchemaDoc.Provider.MicrosoftSqlServer.Helpers;
    using MSSqlSchemaDoc.Provider.MicrosoftSqlServer.Resources;

    /// <summary>
    /// Contains the helper methods for view objects.
    /// </summary>
    public partial class SqlScriptingManager
    {
        /// <summary>
        /// Fetches a list of views and extracts the information for each.
        /// </summary>
        /// <param name="connection">An open connection to the database.</param>
        /// <param name="objectType">The view object type.</param>
        private void ScriptViews(SqlConnection connection, DataStructureObjectType objectType)
        {
            // Load data
            StringBuilder query = new StringBuilder(this.queryManager.Views());
            this.InsertWhereClause(query);
            SqlCommand c = new SqlCommand(query.ToString(), connection);

            // Get index data
            // Use a data table, since we need to evaluate it more than once
            StringBuilder indexQuery = new StringBuilder(this.queryManager.ViewIndexes());
            this.InsertWhereClause(indexQuery);
            SqlDataAdapter adapter = new SqlDataAdapter(indexQuery.ToString(), connection);
            DataTable indexData = new DataTable();
            adapter.Fill(indexData);

            using (IDataReader reader = c.ExecuteReader())
            {
                // First, check for data
                bool hasData = false;
                if (reader.Read())
                {
                    hasData = true;
                }

                while (hasData)
                {
                    string name = GetObjectName(reader);
                    this.OnBeginScriptingObject(name);

                    View v = this.ConstructView(reader, ref hasData, indexData);

                    this.OnCompleteScriptingObject(v, name);
                }
            }
        }

        /// <summary>
        /// Construct a view object.
        /// </summary>
        /// <param name="reader">Use the data in the data reader starting at it's current position.</param>
        /// <param name="hasMoreViews">This method will return false if this is the last view in the result set.</param>
        /// <param name="indexData">The index data for the views.</param>
        /// <returns>The constructed view.</returns>
        private View ConstructView(IDataReader reader, ref bool hasMoreViews, DataTable indexData)
        {
            // Create the view object and set properties
            View v = new View()
            {
                Name = DataReaderHelper.GetString(reader, "ObjectName"),
                Schema = DataReaderHelper.GetString(reader, "SchemaName"),
                UsesAnsiNulls = DataReaderHelper.GetBool(reader, "UsesAnsiNulls")
            };

            v.ObjectId = DataReaderHelper.GetInt(reader, "ObjectId").Value;

            v.Definition = DataReaderHelper.GetString(reader, "Definition");
            v.UsesAnsiNulls = DataReaderHelper.GetBool(reader, "UsesAnsiNulls");
            v.UsesQuotedIdentifier = DataReaderHelper.GetBool(reader, "UsesQuotedIdentifier");
            v.IsSchemaBound = DataReaderHelper.GetBool(reader, "IsSchemaBound");
            v.UsesDatabaseCollation = DataReaderHelper.GetBool(reader, "UsesDatabaseCollation");

            // Add the columns
            v.Columns = new Collection<Column>();
            v.Columns.Add(ConstructColumn(new Column(), reader, indexData, null));

            // Get all the columns for the table by looping through the data reader
            string currentObjectId = reader["ObjectId"].ToString();
            while ((hasMoreViews = reader.Read()) && currentObjectId == reader["ObjectId"].ToString())
            {
                v.Columns.Add(ConstructColumn(new Column(), reader, indexData, null));
            }

            // Indexes
            this.AddIndexes(v, indexData);

            // Documentation
            if (this.IncludeDocumentationObjects)
            {
                v.Documentation = this.ConstructViewDocumentation(v);
            }

            return v;
        }

        /// <summary>
        /// Constructs a view documentation object from a view object.
        /// </summary>
        /// <param name="view">The view to document.</param>
        /// <returns>The constructed documentation object.</returns>
        private ViewDocumentation ConstructViewDocumentation(View view)
        {
            ViewDocumentation existingDocumentation;
            ViewDocumentation documentation = this.ConstructDocumentationObject<ViewDocumentation>(out existingDocumentation, view.Name, view.Schema);

            // Columns
            foreach (Column c in view.Columns)
            {
                ColumnDocumentation cd = new ColumnDocumentation() { Name = c.Name };
                cd.Documentation = GetDocumentationFromCollection<ColumnDocumentation>(existingDocumentation.Columns, cd.Name, cd.Schema);
                documentation.Columns.Add(cd);
            }

            // Indexes
            foreach (Index i in view.Indexes)
            {
                IndexDocumentation d = new IndexDocumentation() { Name = i.Name };
                d.Documentation = GetDocumentationFromCollection<IndexDocumentation>(existingDocumentation.Indexes, i.Name, string.Empty);
                documentation.Indexes.Add(d);
            }

            return documentation;
        }
    }
}
